/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */

package simhya.matheval;
import org.sbml.jsbml.ASTNode;
import simhya.matheval.operator.*;
import simhya.matheval.function.Max;
import simhya.matheval.function.Min;

/**
 *
 * @author Luca
 */
public class BinaryOperatorNode extends OperatorNode {

    public BinaryOperatorNode(OperatorDefinition op) {
        super(op, 2);
    }


    @Override
    public boolean canBeConvertedToNumericalExpression() {
        if (operator instanceof And || operator instanceof Or)
            return children[0].canBeConvertedToNumericalExpression()
                && children[1].canBeConvertedToNumericalExpression();
        else if (operator instanceof Greater ||
                 operator instanceof GreaterOrEqual ||
                 operator instanceof Less ||
                 operator instanceof LessOrEqual ||
                 operator instanceof Equal ||
                 operator instanceof NotEqual)
            return true;
        else
            return false;
    }


    @Override
    public ExpressionNode convertToNumericalExpression(Evaluator eval) {
         if (operator instanceof And || operator instanceof Or) {
             ExpressionNode n1 = children[0].convertToNumericalExpression(eval);
             ExpressionNode n2 = children[1].convertToNumericalExpression(eval);
             ExpressionNode n;
             if (operator instanceof Or)
                n = new BinaryFunctionNode(new Max());
             else
                n = new BinaryFunctionNode(new Min());
             n.addChild(n1);
             n.addChild(n2);
             n = eval.checkNodeDefinition(n);
             return n;
         }
         else if (operator instanceof Greater ||
                  operator instanceof GreaterOrEqual ) {
              ExpressionNode n = new BinaryOperatorNode(new Minus());
              //(a>=b) becomes (a-b)
              n.addChild(children[0]);
              n.addChild(children[1]);
              n = eval.checkNodeDefinition(n);
              return n;
         }
         else if (operator instanceof Less ||
                  operator instanceof LessOrEqual) {
             ExpressionNode n = new BinaryOperatorNode(new Minus());
             //(a<=b) becomes (b-a)
              n.addChild(children[1]);
              n.addChild(children[0]);
              n = eval.checkNodeDefinition(n);
              return n;
         }
         else if (operator instanceof Equal ||
                 operator instanceof NotEqual) {
             //we replace equality/inequality with if(a==b,1,-1)/if(a!=b,1,-1)
             ExpressionNode n = new IfNode();
             n.addChild(this);
             n.addChild(new NumericNode(1));
             n.addChild(new NumericNode(-1));
             n = eval.checkNodeDefinition(n);
             return n;
//             throw new EvalException("Cannot convert into numerical form " +
//                "an equality or an inequality");
         }
         else
             throw new EvalException("Cannot convert into numerical form " +
                "a non-logical expression");             
    }

   @Override
    void computeValue() {
        children[0].computeValue();
        children[1].computeValue();
        super.value = operator.compute(children[0].value, children[1].value);
    }

    @Override
    void computeValue(SymbolArray varReference) {
        children[0].computeValue(varReference);
        children[1].computeValue(varReference);
        super.value = operator.compute(children[0].value, children[1].value);
    }

    @Override
    void computeValue(int evaluationCode) {
        if (random || super.evaluationCode != evaluationCode) {
            super.evaluationCode = evaluationCode;
            children[0].computeValue(evaluationCode);
            children[1].computeValue(evaluationCode);
            super.value = operator.compute(children[0].value, children[1].value);
        }
    }

    @Override
    void computeValue(SymbolArray varReference, int evaluationCode) {
        if (random || super.evaluationCode != evaluationCode) {
            super.evaluationCode = evaluationCode;
            children[0].computeValue(varReference,evaluationCode);
            children[1].computeValue(varReference,evaluationCode);
            super.value = operator.compute(children[0].value, children[1].value);
        }
    }
    
    @Override
    void computeValueLocal(SymbolArray localVariables) {
        children[0].computeValueLocal(localVariables);
        children[1].computeValueLocal(localVariables);
        super.value = operator.compute(children[0].value, children[1].value);
    }

    @Override
    void computeValueLocal(SymbolArray localVariables, SymbolArray globalVariables) {
        children[0].computeValueLocal(localVariables,globalVariables);
        children[1].computeValueLocal(localVariables,globalVariables);
        super.value = operator.compute(children[0].value, children[1].value);
    }

    @Override
    void computeValueLocal(SymbolArray localVariables, int evaluationCode) {
        if (random || super.evaluationCode != evaluationCode) {
            super.evaluationCode = evaluationCode;
            children[0].computeValueLocal(localVariables,evaluationCode);
            children[1].computeValueLocal(localVariables,evaluationCode);
            super.value = operator.compute(children[0].value, children[1].value);
        }
    }

    @Override
    void computeValueLocal(SymbolArray localVariables, SymbolArray globalVariables, int evaluationCode) {
        if (random || super.evaluationCode != evaluationCode) {
            super.evaluationCode = evaluationCode;
            children[0].computeValueLocal(localVariables,globalVariables,evaluationCode);
            children[1].computeValueLocal(localVariables,globalVariables,evaluationCode);
            super.value = operator.compute(children[0].value, children[1].value);
        }
    }
    
    @Override
    protected Object clone() throws CloneNotSupportedException {
        BinaryOperatorNode newNode = new BinaryOperatorNode(this.operator);
        this.copyContent(newNode);
        return newNode;
    }

    @Override
    ASTNode convertToJSBML() {
        ASTNode n1,n2,n;
        n1 = children[0].convertToJSBML();
        n2 = children[1].convertToJSBML();
        n = new ASTNode(operator.getSBMLType());
        n.addChild(n1);
        n.addChild(n2);
        return n;
    }
    
    
    private void setContinuityStatus() {
        ExpressionNode left = children[0];
        ExpressionNode right = children[1];
        if (operator.isLogical()) {
            if (operator.getStringRepresentation().equals("&&")) {
                //and case
                if (!left.canChangeContinuously && !right.canChangeContinuously)
                    this.canChangeContinuously = false;
                else if (!left.canChangeContinuously && left.value == 0.0) 
                    //in this case, the term that cannot be modified by continuous variables
                    //is false, so the truth value of the expression will be false.
                    this.canChangeContinuously = false;
                else if (!right.canChangeContinuously && right.value == 0.0)
                    this.canChangeContinuously = false;
                else
                    this.canChangeContinuously = true;
            } else {
                //or case
                if (!left.canChangeContinuously && !right.canChangeContinuously)
                    this.canChangeContinuously = false;
                else if (!left.canChangeContinuously && left.value == 1.0) 
                    //in this case, the term that cannot be modified by continuous variables
                    //is true, so the truth value of the expression will be true.
                    this.canChangeContinuously = false;
                else if (!right.canChangeContinuously && right.value == 1.0)
                    this.canChangeContinuously = false;
                else
                    this.canChangeContinuously = true;
            }
        } else 
            this.canChangeContinuously = left.canChangeContinuously || right.canChangeContinuously;
    }
    
    
    //if the operator is logical
    //and: if left and right are false: false
    //if one false and one true, false if the false one has value false.
    //if both true, then true.
    //or: if both false, then false. If one true, then false if the false one has value true. otherwise true
    //true if both true.
    //other operators: true if one child is true.

    @Override
    void computeContinuousChangeStatus() {
        children[0].computeContinuousChangeStatus();
        children[1].computeContinuousChangeStatus();
        setContinuityStatus();
    }
    
    @Override
    void computeContinuousChangeStatus(SymbolArray varReference) {
        children[0].computeContinuousChangeStatus(varReference);
        children[1].computeContinuousChangeStatus(varReference);
        setContinuityStatus();
    }

    @Override
    void computeContinuousChangeStatusLocal(SymbolArray localVariables) {
        children[0].computeContinuousChangeStatusLocal(localVariables);
        children[1].computeContinuousChangeStatusLocal(localVariables);
        setContinuityStatus();
    }

    @Override
    void computeContinuousChangeStatusLocal(SymbolArray localVariables, SymbolArray globalVariables) {
        children[0].computeContinuousChangeStatusLocal(localVariables,globalVariables);
        children[1].computeContinuousChangeStatusLocal(localVariables,globalVariables);
        setContinuityStatus();
    }

    String toJavaCode() {
        String s = "";
        String c1 = (!this.isLogicalExpression() && children[0].isLogicalExpression() ?
                    "(" + children[0].toJavaCode() + "? 1.0 : 0.0)"  : 
                    children[0].toJavaCode() );
        String c2 = (!this.isLogicalExpression() && children[1].isLogicalExpression() ?
                    "(" + children[1].toJavaCode() + "? 1.0 : 0.0)"  : 
                    children[1].toJavaCode() );
        if (operator.functionInJava()) {
            s = operator.toJavaCode() + "(";
            s += c1 + " ";
            s += ","; 
            s += c2 + ")";
        } else {
            s = "(";
            s += c1 + " ";
            s += operator.toJavaCode() + " "; 
            s += c2 + ")";
        }
        return s;
    }
    
    
}
